package com.mbc.hr.manager;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.NoResultException;
import javax.persistence.TemporalType;

import org.json.JSONArray;
import org.json.JSONObject;

import com.mbc.hr.data.HRContants;
import com.mbc.hr.data.OverTimeDataList;
import com.mbc.hr.data.OvertimeApprovedData;
import com.mbc.hr.entities.Employee;
import com.mbc.hr.entities.Overtime;
import com.mbc.hr.exception.ExistingOverTimeException;
import com.mbc.hr.exception.NotEmployeeException;
import com.mbc.hr.param.OvertimeCriteria;
import com.mbc.common.exception.DataNotFoundException;
import com.mbc.common.manager.AbstractMgr;
import com.mbc.common.utils.DateUtils;

public class OvertimeMgr extends AbstractMgr{
	
	public OvertimeMgr(){}
	
	public OvertimeMgr(String sessionId){
		this.sessionId = sessionId;
	}
	
	public void requestOverTime(Overtime ot, String loginId)throws Exception{
		openConnection();
		try{
			beginTransaction();
			Employee l_emp = null;
			try{
				l_emp =	entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class)
						.setParameter("employeeNo", loginId)
						.getSingleResult();
			} catch (NoResultException innerEx) {
				throw new NotEmployeeException("This login[" + loginId + "] user is not an employee.");
			}
			
			Overtime l_overTime = null;
			try {
				Date[] l_range = DateUtils.getRangeForDate(ot.getOtDate());
				l_overTime = entityManager.createNamedQuery("Overtime.findByDateNEmployeeId", Overtime.class)
						.setParameter("startDate", l_range[0])
						.setParameter("endDate", l_range[1])
						.setParameter("employeeId", ot.getEmployeeId())
						.getSingleResult();
			} catch (NoResultException innerEx) {}
			
			if(l_overTime == null){
				ot.setEmployeeId(l_emp.getEmployeeID());
				ot.setEmployeeName(l_emp.getName());
				ot.setBranchId(l_emp.getBranchId());
				ot.setCreatedDate(new Date());
				ot.setStatus(HRContants.OTStatus.PENDING);
				entityManager.persist(ot);
			}
			else
				throw new ExistingOverTimeException("There is an over time on " + ot.getOtDate() + ".");
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void updateOverTime(Overtime ot, String loginId)throws Exception{
		openConnection();
		try{
			beginTransaction();
			ot.setModifiedDate(new Date());
			
			Overtime l_ot = entityManager.find(Overtime.class, ot.getotId());
			if(l_ot.getStatus() != HRContants.OTStatus.PENDING)
				throw new DataNotFoundException("There is no over time on " + ot.getOtDate() + ".");
			entityManager.merge(ot);
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void deleteOverTime(long otid)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Overtime l_ot = entityManager.find(Overtime.class, otid);
			if(l_ot.getStatus() != HRContants.OTStatus.PENDING)
				throw new DataNotFoundException("There is no pending over time on " + l_ot.getOtDate() + ".");
			entityManager.remove(l_ot);
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void approvedOvertimes(List<OvertimeApprovedData> dataList, boolean isApproved, String loginId)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Employee l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class)
					.setParameter("employeeNo", loginId)
					.getSingleResult();
			
			for (OvertimeApprovedData l_approvedData : dataList) {
				Overtime l_overTime = entityManager.find(Overtime.class, l_approvedData.getOvertimeID());
				l_overTime.setReason(l_approvedData.getReason());
				if(l_overTime.getStatus() == HRContants.OTStatus.PENDING){
					l_overTime.setApprovedBy(l_emp.getEmployeeID());
					l_overTime.setApprovedDate(new Date());
					l_overTime.setStatus(isApproved ? HRContants.OTStatus.APPROVED : HRContants.OTStatus.REJECTED);
					
					entityManager.merge(l_overTime);
				}
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
		
	public void approvedOverTime(Overtime ot, boolean isApproved, String loginId)throws Exception{
		openConnection();
		try {
			beginTransaction();

			Long l_count = entityManager.createNamedQuery("Overtime.getOTCountWithStatus", Long.class)
					.setParameter("employeeId", ot.getEmployeeId())
					.setParameter("otdate", ot.getOtDate(), TemporalType.DATE)
					.setParameter("status", HRContants.OTStatus.PENDING)
					.getSingleResult();
			if(l_count <= 0)
				throw new ExistingOverTimeException("There is no pening over time on " + ot.getOtDate() + ".");
			
			if(isApproved){
				ot.setStatus(HRContants.OTStatus.APPROVED);
			}
			else{
				ot.setStatus(HRContants.OTStatus.REJECTED);
			}
			
			entityManager.merge(ot);
			commitTransaction();
			
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public Overtime findOverTime(long id)throws Exception{
		openConnection();
		try {
			return entityManager.find(Overtime.class, id);
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public OverTimeDataList getOvertimes(OvertimeCriteria cri, String empNo)throws Exception{
		openConnection();
		try {
			Employee l_emp = null;
			String l_jpql = "";
			if(cri.isForApproval())
				l_jpql = "select ot, ott.description from Overtime ot, OvertimeType ott, Employee e where  ot.otType = ott.otTypeID and ot.employeeId = e.employeeID ";
			else
				l_jpql = "Select ot, ott.description From Overtime ot, OvertimeType ott Where ot.otType = ott.otTypeID ";
			try {
				l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class)
					.setParameter("employeeNo", empNo)
					.getSingleResult();
				cri.setEmployeeId(l_emp.getEmployeeID());
			} catch (NoResultException e) {
				
			}
			if(l_emp != null){
				if(cri.getEmployeeId() != 0){
					if(cri.isForApproval()){
						l_jpql += " And e.supervisor = " + l_emp.getEmployeeID();
					}else{
						l_jpql += " And ot.employeeId = " + l_emp.getEmployeeID();
					}
				}
				if(cri.getovertimeStatus() != 0){
					l_jpql += " And ot.status = " + cri.getovertimeStatus();
				}
				
				List<Object[]> l_resultList = entityManager.createQuery(l_jpql, Object[].class)
						.setFirstResult(cri.getIndex())
						.setMaxResults(cri.getSize())
						.getResultList();
			
				List<Overtime> l_otList = new ArrayList<Overtime>();
				for (Object[] l_result : l_resultList) {
					Overtime l_ot = (Overtime)l_result[0];
					l_ot.setOtTypeDescription(l_result[1].toString());
					l_otList.add(l_ot);
				}
				
				OverTimeDataList l_dataList = new OverTimeDataList();
				l_dataList.setEntityList(l_otList);
				l_dataList.setTotalRecord((long)l_resultList.size()+1);
				
	            return l_dataList;
			} else {
				return null;
			}
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	@SuppressWarnings({ "unchecked" })
	public JSONArray getMobileOvertimeList(byte overtimeStatus, long overtimeId, String loginId, int size, boolean myOvertime)throws Exception{
		openConnection();
		try {
			String l_jpql = "select ot.otId, ot.employeeName, ot.task, ot.reason, ot.otDate, ot.otMinutes, ot.otType from ";
			String l_cri=" ot.status = " + overtimeStatus;
			Employee l_emp = entityManager.createNamedQuery("Employee.findByEmployeeNo", Employee.class)
					.setParameter("employeeNo", loginId)
					.getSingleResult();
			if(myOvertime){
				l_cri = " ot.employeeId = " + l_emp.getEmployeeID();
				if(overtimeId != 0)
					l_cri = " and ot.otId < " + overtimeId;
				l_jpql += " Overtime ot WHERE " + l_cri;
			}else{
				l_cri += " and e.supervisor = " + l_emp.getEmployeeID();
				if(overtimeId != 0)
					l_cri += " and ot.otId < " + overtimeId;
				l_jpql += " Overtime ot, Employee e WHERE " + l_cri + " AND ot.employeeId = e.employeeID";
			}
			
			List<Object[]> l_resultList = entityManager.createQuery(l_jpql)
					.setFirstResult(0)
					.setMaxResults(size)
					.getResultList();
			
			SimpleDateFormat l_dateTimeFormat = new SimpleDateFormat("yyyyMMddHHmmss");
			JSONArray l_leaveDataList = new JSONArray();
			for (Object[] l_objArr : l_resultList) {
				JSONObject l_data = new JSONObject();
				l_data.put("otId", Long.parseLong(l_objArr[0].toString()));
				l_data.put("employeeName", l_objArr[1].toString());
				l_data.put("task", l_objArr[2].toString());
				l_data.put("reason", l_objArr[3].toString());
				l_data.put("otDate", l_dateTimeFormat.format((Date)l_objArr[4]));
				l_data.put("otMinutes", Integer.parseInt(l_objArr[5].toString()));
				l_data.put("otType", Long.parseLong(l_objArr[6].toString()));
				l_leaveDataList.put(l_data);
			}
			return l_leaveDataList;
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
}
